using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;

using HundredMilesSoftware.UltraID3Lib;
using System.Reflection;


namespace MP3TagBackup
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                if (args.Length > 0 && args[0].Equals("-?"))
                {
                    //Assembly.GetExecutingAssembly().GetCustomAttributes(AssemblyVersionAttribute
                    Console.WriteLine("MP3TagBackup");
                    Console.WriteLine("Version: ");
                    Console.WriteLine("Usage:  MP3TagBackup [-recurse] [outputfile] [directory]");
                    Console.WriteLine("        MP3TagBackup -restore [backupfile] [directory]");
                    Console.WriteLine("        MP3TagBackup -erase [directory]");
                    Console.WriteLine("  Creates an XML file containing the ID3 tags from all .mp3 files in the directory.");
                    Console.WriteLine("Default outputfile/backupfile is mp3tags.xml.  Default directory is the current directory.");
                    Console.WriteLine("The -recurse option will work with all .mp3 files in subdirectories as well.");
                    Console.WriteLine("The -restore option will restore the backed up tags for the files in the directory.");
                    Console.WriteLine("The -erase option will erase all MP3 tags from all .mp3 files in the directory.");
                    Console.WriteLine();
                    Console.WriteLine("MP3TagBackup uses UltraID3Lib, an MP3 ID3 Tag Editor and MPEG Info Reader Library");
                    Console.WriteLine("Copyright 2002 - 2006 Hundred Miles Software (Mitchell S. Honnert)");
                    Console.WriteLine("www.UltraID3Lib.com, www.HundredMilesSoftware.com");
                    return;
                }

                string backupFile = "mp3tags.xml";
                string directory = Directory.GetCurrentDirectory();
                bool restore = false;
                bool erase = false;
                bool recurse = false;

                int state = 0;
                for (int i = 0; i < args.Length; i++)
                {
                    if (args[i].Equals("-restore"))
                    {
                        restore = true;
                    }
                    else if (args[i].Equals("-recurse"))
                    {
                        recurse = true;
                    }
                    else if (args[i].Equals("-erase"))
                    {
                        erase = true;
                        state = 1;
                    }
                    else if (args[i].StartsWith("-"))
                    {
                        Console.WriteLine("Unknown argument: " + args[i] + "  (use -? switch for help)");
                        return;
                    }
                    else
                    {
                        if (state == 0)
                        {
                            backupFile = args[i];
                            state++;
                        }
                        else if (state == 1)
                        {
                            directory = args[i];
                            state++;
                        }
                        else
                        {
                            Console.WriteLine("Unknown argument: " + args[i] + "  (use -? switch for help)");
                            return;
                        }
                    }
                }

                if (restore)
                {
                    XmlDocument doc = new XmlDocument();
                    doc.Load(backupFile);
                    XmlNode root = doc.SelectSingleNode("/mp3files");
                    if (root == null)
                    {
                        Console.WriteLine("Invalid backup file (does not contain mp3files element)");
                        return;
                    }
                    foreach (XmlNode fileNode in root.ChildNodes)
                    {
                        string file = fileNode.Attributes["filename"].Value;
                        if (File.Exists(file))
                        {
                            Console.WriteLine("Restoring tags to file " + file);

                            UltraID3 tags = new UltraID3();
                            tags.Read(file);
                            tags.ID3v23Tag.Frames.Clear();
                            tags.ID3v1Tag.WriteFlag = false;
                            tags.ID3v23Tag.WriteFlag = true;
                            foreach (XmlNode node in fileNode)
                            {
                                ID3Frame frame = CreateID3Frame(node);
                                if (frame != null)
                                {
                                    tags.ID3v23Tag.Frames.Add(frame);
                                }
                                else
                                {
                                    Console.WriteLine("Unable to write frame for XML node " + node.Name);
                                }
                            }
                            tags.Write();
                        }
                        else
                        {
                            Console.WriteLine("Unable to find MP3 file " + file);
                        }
                    }
                }
                else
                {
                    string[] files = Directory.GetFiles(directory, "*.mp3", (recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly));
                    if (files.Length == 0)
                    {
                        Console.WriteLine("No MP3 files found.  (use -? switch for help)");
                        return;
                    }

                    XmlDocument doc = null;
                    XmlElement rootElement = null;
                    if (!erase)
                    {
                        doc = new XmlDocument();
                        rootElement = doc.CreateElement("mp3files");
                    }

                    foreach (string file in files)
                    {
                        if (erase)
                        {
                            Console.WriteLine("Erasing tags from file " + file);
                            UltraID3 tags = new UltraID3();
                            tags.Read(file);
                            tags.ID3v1Tag.WriteFlag = false;
                            tags.ID3v23Tag.WriteFlag = false;
                            tags.Write();
                        }
                        else
                        {
                            Console.WriteLine("Reading tags from file " + file);

                            XmlElement fileElement = doc.CreateElement("mp3file");
                            fileElement.SetAttribute("filename", file.Substring(directory.Length + 1));

                            UltraID3 tags = new UltraID3();
                            tags.Read(file);
                            foreach (ID3Frame frame in tags.ID3v23Tag.Frames)
                            {
                                XmlElement frameElement = CreateFrameElement(doc, frame);
                                if (frameElement != null)
                                {
                                    fileElement.AppendChild(frameElement);
                                }
                                else
                                {
                                    Console.WriteLine("Unsupported ID3 frame: " + frame);
                                }
                            }
                            rootElement.AppendChild(fileElement);
                        }
                    }
                    if (!erase)
                    {
                        doc.AppendChild(rootElement);
                        doc.Save(backupFile);
                    }
                }

            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception: " + ex.Message);
                Console.WriteLine(ex.StackTrace);
            }
        }



        private static XmlElement CreateFrameElement(XmlDocument doc, ID3Frame frame)
        {
            XmlElement frameElement = null;
            switch (frame.FrameType)
            {
                case FrameTypes.Unknown:
                    break;

                case FrameTypes.Artist:
                    frameElement = doc.CreateElement("artist");
                    frameElement.InnerText = ((ID3ArtistFrame)frame).Artist;
                    break;

                case FrameTypes.Album:
                    frameElement = doc.CreateElement("album");
                    frameElement.InnerText = ((ID3AlbumFrame)frame).Album;
                    break;

                case FrameTypes.Year:
                    frameElement = doc.CreateElement("year");
                    frameElement.InnerText = ((ID3YearFrame)frame).Year.ToString();
                    break;

                case FrameTypes.Genre:
                    frameElement = doc.CreateElement("genre");
                    frameElement.InnerText = ((ID3GenreFrame)frame).Genre;
                    break;

                case FrameTypes.AudioEncryption:
                    break;

                case FrameTypes.Picture:
                    break;

                case FrameTypes.Comments:
                    frameElement = doc.CreateElement("comment");
                    frameElement.SetAttribute("description", ((ID3CommentsFrame)frame).Description);
                    frameElement.InnerText = ((ID3CommentsFrame)frame).Comments;
                    break;

                case FrameTypes.Commercial:
                    break;

                case FrameTypes.EncryptionMethodRegistration:
                    break;

                case FrameTypes.Equalization:
                    break;

                case FrameTypes.EventTimingCodes:
                    break;

                case FrameTypes.GeneralEncapsulatedObject:
                    break;

                case FrameTypes.GroupIdentificationRegistration:
                    break;

                case FrameTypes.InvolvedPeopleList:
                    frameElement = doc.CreateElement("involvedPeople");
                    foreach (InvolvedPerson person in ((ID3InvolvedPeopleListFrame)frame).InvolvedPersonList)
                    {
                        XmlElement personElement = doc.CreateElement("involvedPerson");
                        personElement.SetAttribute("type", person.InvolvementType);
                        personElement.InnerText = person.PersonName;
                        frameElement.AppendChild(personElement);
                    }
                    break;

                case FrameTypes.LinkedInformation:
                    break;

                case FrameTypes.CompactDiscIdentifier:
                    frameElement = doc.CreateElement("compactDiscIdentifier");
                    frameElement.SetAttribute("MCDIFrameFormat", ((ID3CompactDiscIdentifierFrame)frame).MCDIFrameFormat.ToString());
                    if (((ID3CompactDiscIdentifierFrame)frame).MCDIFrameFormat == MCDIFrameFormats.ID3Standard)
                    {
                        frameElement.SetAttribute("HeaderBytes", ByteArrayToString(((ID3CompactDiscIdentifierFrame)frame).HeaderBytes));
                        frameElement.SetAttribute("LeadOutBytes", ByteArrayToString(((ID3CompactDiscIdentifierFrame)frame).LeadOutBytes));
                        // should be an ArrayList of byte[]
                        foreach (byte[] bytes in ((ID3CompactDiscIdentifierFrame)frame).TrackFrameAddresses)
                        {
                            XmlElement addressElement = doc.CreateElement("trackFrameAddress");
                            addressElement.InnerText = ByteArrayToString(bytes);
                            frameElement.AppendChild(addressElement);
                        }
                    }
                    else
                    {
                        frameElement.SetAttribute("WindowsMediaPlayerCode", ((ID3CompactDiscIdentifierFrame)frame).WindowsMediaPlayerCode);
                    }
                    break;

                case FrameTypes.MPEGLocationLookupTable:
                    break;

                case FrameTypes.Ownership:
                    break;

                case FrameTypes.Private:
                    frameElement = doc.CreateElement("private");
                    frameElement.SetAttribute("owner", ((ID3PrivateFrame)frame).OwnerIdentifier);
                    frameElement.InnerText = ByteArrayToString(((ID3PrivateFrame)frame).PrivateData);
                    break;

                case FrameTypes.PlayCounter:
                    frameElement = doc.CreateElement("playCounter");
                    frameElement.InnerText = ((ID3PlayCounterFrame)frame).Counter.ToString();
                    break;

                case FrameTypes.Popularimeter:
                    frameElement = doc.CreateElement("popularimeter");
                    frameElement.SetAttribute("email", ((ID3PopularimeterFrame)frame).EMailAddress);
                    frameElement.SetAttribute("counter", ((ID3PopularimeterFrame)frame).Counter.ToString());
                    frameElement.InnerText = ((ID3PopularimeterFrame)frame).Rating.ToString();
                    break;

                case FrameTypes.PositionSychronization:
                    break;

                case FrameTypes.RecommendedBufferSize:
                    break;

                case FrameTypes.RelativeVolumeAdjustment:
                    frameElement = doc.CreateElement("relativeVolumeAdjustment");
                    frameElement.SetAttribute("BassChannelPeakVolume", ((ID3RelativeVolumeAdjustmentFrame)frame).BassChannelPeakVolume.ToString());
                    frameElement.SetAttribute("BassChannelVolumeAdjustmentType", ((ID3RelativeVolumeAdjustmentFrame)frame).BassChannelRelativeVolumeAdjustmentType.ToString());
                    frameElement.SetAttribute("BassChannelVolumeChange", ((ID3RelativeVolumeAdjustmentFrame)frame).BassChannelRelativeVolumeChange.ToString());
                    frameElement.SetAttribute("CenterChannelPeakVolume", ((ID3RelativeVolumeAdjustmentFrame)frame).CenterChannelPeakVolume.ToString());
                    frameElement.SetAttribute("CenterChannelVolumeAdjustmentType", ((ID3RelativeVolumeAdjustmentFrame)frame).CenterChannelRelativeVolumeAdjustmentType.ToString());
                    frameElement.SetAttribute("CenterChannelVolumeChange", ((ID3RelativeVolumeAdjustmentFrame)frame).CenterChannelRelativeVolumeChange.ToString());
                    frameElement.SetAttribute("LeftbackChannelPeakVolume", ((ID3RelativeVolumeAdjustmentFrame)frame).LeftBackChannelPeakVolume.ToString());
                    frameElement.SetAttribute("LeftbackChannelVolumeAdjustmentType", ((ID3RelativeVolumeAdjustmentFrame)frame).LeftBackChannelRelativeVolumeAdjustmentType.ToString());
                    frameElement.SetAttribute("LeftbackChannelVolumeChange", ((ID3RelativeVolumeAdjustmentFrame)frame).LeftBackChannelRelativeVolumeChange.ToString());
                    frameElement.SetAttribute("LeftChannelPeakVolume", ((ID3RelativeVolumeAdjustmentFrame)frame).LeftChannelPeakVolume.ToString());
                    frameElement.SetAttribute("LeftChannelVolumeAdjustmentType", ((ID3RelativeVolumeAdjustmentFrame)frame).LeftChannelRelativeVolumeAdjustmentType.ToString());
                    frameElement.SetAttribute("LeftChannelVolumeChange", ((ID3RelativeVolumeAdjustmentFrame)frame).LeftChannelRelativeVolumeChange.ToString());
                    frameElement.SetAttribute("RightbackChannelPeakVolume", ((ID3RelativeVolumeAdjustmentFrame)frame).RightBackChannelPeakVolume.ToString());
                    frameElement.SetAttribute("RightbackChannelVolumeAdjustmentType", ((ID3RelativeVolumeAdjustmentFrame)frame).RightBackChannelRelativeVolumeAdjustmentType.ToString());
                    frameElement.SetAttribute("RightbackChannelVolumeChange", ((ID3RelativeVolumeAdjustmentFrame)frame).RightBackChannelRelativeVolumeChange.ToString());
                    frameElement.SetAttribute("RightChannelPeakVolume", ((ID3RelativeVolumeAdjustmentFrame)frame).RightChannelPeakVolume.ToString());
                    frameElement.SetAttribute("RightChannelVolumeAdjustmentType", ((ID3RelativeVolumeAdjustmentFrame)frame).RightChannelRelativeVolumeAdjustmentType.ToString());
                    frameElement.SetAttribute("RightChannelVolumeChange", ((ID3RelativeVolumeAdjustmentFrame)frame).RightChannelRelativeVolumeChange.ToString());
                    break;

                case FrameTypes.Reverb:
                    frameElement = doc.CreateElement("reverb");
                    frameElement.SetAttribute("PremixLeftToRight", ((ID3ReverbFrame)frame).PremixLeftToRight.ToString());
                    frameElement.SetAttribute("PremixRightToLeft", ((ID3ReverbFrame)frame).PremixRightToLeft.ToString());
                    frameElement.SetAttribute("ReverbBouncesLeft", ((ID3ReverbFrame)frame).ReverbBouncesLeft.ToString());
                    frameElement.SetAttribute("ReverbBouncesRight", ((ID3ReverbFrame)frame).ReverbBouncesRight.ToString());
                    frameElement.SetAttribute("ReverbFeedbackLeftToLeft", ((ID3ReverbFrame)frame).ReverbFeedbackLeftToLeft.ToString());
                    frameElement.SetAttribute("ReverbFeedbackLeftToRight", ((ID3ReverbFrame)frame).ReverbFeedbackLeftToRight.ToString());
                    frameElement.SetAttribute("ReverbFeedbackRightToLeft", ((ID3ReverbFrame)frame).ReverbFeedbackRightToLeft.ToString());
                    frameElement.SetAttribute("ReverbFeedbackRightToRight", ((ID3ReverbFrame)frame).ReverbFeedbackRightToRight.ToString());
                    frameElement.SetAttribute("ReverbLeftMS", ((ID3ReverbFrame)frame).ReverbLeftMS.ToString());
                    frameElement.SetAttribute("ReverbRightMS", ((ID3ReverbFrame)frame).ReverbRightMS.ToString());
                    break;

                case FrameTypes.SynchronizedLyrics:
                    frameElement = doc.CreateElement("synchronizedLyrics");
                    frameElement.SetAttribute("ContentType", ((ID3SynchronizedLyricsFrame)frame).ContentType.ToString());
                    frameElement.SetAttribute("Description", ((ID3SynchronizedLyricsFrame)frame).Description);
                    frameElement.SetAttribute("Language", ((ID3SynchronizedLyricsFrame)frame).Language);
                    frameElement.SetAttribute("PositionUnit", ((ID3SynchronizedLyricsFrame)frame).PositionUnit.ToString());
                    foreach (SynchronizedLyric lyric in ((ID3SynchronizedLyricsFrame)frame).SynchronizedLyrics)
                    {
                        XmlElement lyricElement = doc.CreateElement("lyric");
                        lyricElement.SetAttribute("position", lyric.Position.ToString());
                        lyricElement.InnerText = lyric.Lyric;
                        frameElement.AppendChild(lyricElement);
                    }
                    break;

                case FrameTypes.SynchronizedTempoCodes:
                    break;

                case FrameTypes.BeatsPerMinute:
                    frameElement = doc.CreateElement("beatsPerMinute");
                    frameElement.InnerText = ((ID3BeatsPerMinuteFrame)frame).BeatsPerMinute.ToString();
                    break;

                case FrameTypes.Composers:
                    frameElement = doc.CreateElement("composers");
                    foreach (String composer in ((ID3ComposersFrame)frame).Composers)
                    {
                        XmlElement composerElement = doc.CreateElement("composer");
                        composerElement.InnerText = composer;
                        frameElement.AppendChild(composerElement);
                    }
                    break;

                case FrameTypes.CopyrightMessage:
                    frameElement = doc.CreateElement("copyrightMessage");
                    frameElement.InnerText = ((ID3CopyrightMessageFrame)frame).CopyrightMessage;
                    break;

                case FrameTypes.RecordingDayMonth:
                    frameElement = doc.CreateElement("recordingDayMonth");
                    frameElement.SetAttribute("month", ((ID3RecordingDayMonthFrame)frame).Month.ToString());
                    frameElement.SetAttribute("day", ((ID3RecordingDayMonthFrame)frame).Day.ToString());
                    break;

                case FrameTypes.PlayListDelayMilliseconds:
                    frameElement = doc.CreateElement("playlistDelayMilliseconds");
                    frameElement.InnerText = ((ID3PlaylistDelayMillisecondsFrame)frame).PlaylistDelayMilliseconds.ToString();
                    break;

                case FrameTypes.EncodedBy:
                    frameElement = doc.CreateElement("encodedBy");
                    frameElement.InnerText = ((ID3EncodedByFrame)frame).EncodedBy;
                    break;

                case FrameTypes.Lyricist:
                    frameElement = doc.CreateElement("lyricist");
                    frameElement.InnerText = ((ID3LyricistFrame)frame).Lyricist;
                    break;

                case FrameTypes.FileType:
                    frameElement = doc.CreateElement("fileType");
                    frameElement.InnerText = ((ID3FileTypeFrame)frame).FileType;
                    break;

                case FrameTypes.DurationHoursMinutes:
                    frameElement = doc.CreateElement("durationHoursMinutes");
                    frameElement.InnerText = ((ID3DurationHoursMinutesFrame)frame).Duration.ToString();
                    break;

                case FrameTypes.DurationMilliseconds:
                    frameElement = doc.CreateElement("durationMilliseconds");
                    frameElement.InnerText = ((ID3DurationMillisecondsFrame)frame).DurationMilliseconds.ToString();
                    break;

                case FrameTypes.ContentGroupDescription:
                    frameElement = doc.CreateElement("contentGroupDescription");
                    frameElement.InnerText = ((ID3ContentGroupDescriptionFrame)frame).ContentGroupDescription;
                    break;

                case FrameTypes.Title:
                    frameElement = doc.CreateElement("title");
                    frameElement.InnerText = ((ID3TitleFrame)frame).Title;
                    break;

                case FrameTypes.Subtitle:
                    frameElement = doc.CreateElement("subtitle");
                    frameElement.InnerText = ((ID3SubtitleFrame)frame).Subtitle;
                    break;

                case FrameTypes.InitialKey:
                    frameElement = doc.CreateElement("initialKey");
                    frameElement.InnerText = ((ID3InitialKeyFrame)frame).InitialKey;
                    break;

                case FrameTypes.Languages:
                    frameElement = doc.CreateElement("languages");
                    frameElement.InnerText = ((ID3LanguagesFrame)frame).Languages;
                    break;

                case FrameTypes.MediaType:
                    frameElement = doc.CreateElement("mediaType");
                    frameElement.InnerText = ((ID3MediaTypeFrame)frame).MediaType;
                    break;

                case FrameTypes.OriginalAlbum:
                    frameElement = doc.CreateElement("originalAlbum");
                    frameElement.InnerText = ((ID3OriginalAlbumFrame)frame).OriginalAlbum;
                    break;

                case FrameTypes.OriginalFileName:
                    frameElement = doc.CreateElement("originalFileName");
                    frameElement.InnerText = ((ID3OriginalFileNameFrame)frame).OriginalFileName;
                    break;

                case FrameTypes.OriginalLyricist:
                    frameElement = doc.CreateElement("originalLyricist");
                    frameElement.InnerText = ((ID3OriginalLyricistFrame)frame).OriginalLyricist;
                    break;

                case FrameTypes.OriginalArtist:
                    frameElement = doc.CreateElement("originalArtist");
                    frameElement.InnerText = ((ID3OriginalArtistFrame)frame).OriginalArtist;
                    break;

                case FrameTypes.OriginalYear:
                    frameElement = doc.CreateElement("originalYear");
                    frameElement.InnerText = ((ID3OriginalYearFrame)frame).OriginalYear;
                    break;

                case FrameTypes.FileOwner:
                    frameElement = doc.CreateElement("fileOwner");
                    frameElement.InnerText = ((ID3FileOwnerFrame)frame).FileOwner;
                    break;

                case FrameTypes.Band:
                    frameElement = doc.CreateElement("band");
                    frameElement.InnerText = ((ID3BandFrame)frame).Band;
                    break;

                case FrameTypes.Conductor:
                    frameElement = doc.CreateElement("conductor");
                    frameElement.InnerText = ((ID3ConductorFrame)frame).Conductor;
                    break;

                case FrameTypes.InterpretedBy:
                    frameElement = doc.CreateElement("interpretedBy");
                    frameElement.InnerText = ((ID3InterpretedByFrame)frame).InterpretedBy;
                    break;

                case FrameTypes.PartOfSet:
                    frameElement = doc.CreateElement("partOfSet");
                    frameElement.SetAttribute("PartNum", ((ID3PartOfSetFrame)frame).PartNum.ToString());
                    frameElement.SetAttribute("PartCount", ((ID3PartOfSetFrame)frame).PartCount.ToString());
                    break;

                case FrameTypes.Publisher:
                    frameElement = doc.CreateElement("publisher");
                    frameElement.InnerText = ((ID3PublisherFrame)frame).Publisher;
                    break;

                case FrameTypes.TrackNum:
                    frameElement = doc.CreateElement("trackNum");
                    frameElement.SetAttribute("TrackNum", ((ID3TrackNumFrame)frame).TrackNum.ToString());
                    frameElement.SetAttribute("TrackCount", ((ID3TrackNumFrame)frame).TrackCount.ToString());
                    break;

                case FrameTypes.RecordingDates:
                    frameElement = doc.CreateElement("recordingDates");
                    frameElement.InnerText = ((ID3RecordingDatesFrame)frame).RecordingDates;
                    break;

                case FrameTypes.InternetRadioStationName:
                    frameElement = doc.CreateElement("internetRadioStationName");
                    frameElement.InnerText = ((ID3InternetRadioStationNameFrame)frame).InternetRadioStationName;
                    break;

                case FrameTypes.InternetRadioStationOwner:
                    frameElement = doc.CreateElement("internetRadioStationOwner");
                    frameElement.InnerText = ((ID3InternetRadioStationOwnerFrame)frame).InternetRadioStationOwner;
                    break;

                case FrameTypes.SizeBytes:
                    frameElement = doc.CreateElement("sizeBytes");
                    frameElement.InnerText = ((ID3SizeBytesFrame)frame).SizeBytes.ToString();
                    break;

                case FrameTypes.InternationalStandardRecordingCode:
                    frameElement = doc.CreateElement("internationalStandardRecordingCode");
                    frameElement.InnerText = ((ID3InternationalStandardRecordingCodeFrame)frame).InternationalStandardRecordingCode;
                    break;

                case FrameTypes.EncoderSettings:
                    frameElement = doc.CreateElement("encoderSettings");
                    frameElement.InnerText = ((ID3EncoderSettingsFrame)frame).EncoderSettings;
                    break;

                case FrameTypes.UserDefinedText:
                    frameElement = doc.CreateElement("userDefinedText");
                    frameElement.InnerText = ((ID3UserDefinedTextFrame)frame).UserDefinedText;
                    break;

                case FrameTypes.UniqueFileID:
                    frameElement = doc.CreateElement("uniqueFileID");
                    frameElement.SetAttribute("ownerID", ((ID3UniqueFileIDFrame)frame).OwnerIdentifier);
                    frameElement.InnerText = ByteArrayToString(((ID3UniqueFileIDFrame)frame).Identifier);
                    break;

                case FrameTypes.TermsOfUse:
                    frameElement = doc.CreateElement("termsOfUse");
                    frameElement.InnerText = ((ID3TermsOfUseFrame)frame).TermsOfUse;
                    break;

                case FrameTypes.UnsyncedLyrics:
                    frameElement = doc.CreateElement("unsyncedLyrics");
                    frameElement.InnerText = ((ID3UnsynchedLyricsFrame)frame).UnsynchedLyrics;
                    break;

                case FrameTypes.CommercialInformationWebAddress:
                    frameElement = doc.CreateElement("commercialInformationWebAddress");
                    frameElement.InnerText = ((ID3CommercialInformationWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.CopyrightInformationWebAddress:
                    frameElement = doc.CreateElement("copyrightInformationWebAddress");
                    frameElement.InnerText = ((ID3CopyrightInformationWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.OfficialAudioFileWebAddress:
                    frameElement = doc.CreateElement("officialAudioFileWebAddress");
                    frameElement.InnerText = ((ID3OfficialAudioFileWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.OfficialArtistWebAddress:
                    frameElement = doc.CreateElement("officialArtistWebAddress");
                    frameElement.InnerText = ((ID3OfficialArtistWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.OfficialAudioSourceWebAddress:
                    frameElement = doc.CreateElement("officialAudioSourceWebAddress");
                    frameElement.InnerText = ((ID3OfficialAudioSourceWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.OfficialInternetRadioStationWebAddress:
                    frameElement = doc.CreateElement("officialInternetRadioStationWebAddress");
                    frameElement.InnerText = ((ID3OfficialInternetRadioStationWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.PaymentWebAddress:
                    frameElement = doc.CreateElement("paymentWebAddress");
                    frameElement.InnerText = ((ID3PaymentWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.PublisherWebAddress:
                    frameElement = doc.CreateElement("publisherWebAddress");
                    frameElement.InnerText = ((ID3PublisherWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.UserDefinedWebAddress:
                    frameElement = doc.CreateElement("userDefinedWebAddress");
                    frameElement.SetAttribute("description", ((ID3UserDefinedWebAddressFrame)frame).Description);
                    frameElement.InnerText = ((ID3UserDefinedWebAddressFrame)frame).WebAddress;
                    break;
            }
            return frameElement;
        }


        private static ID3Frame CreateID3Frame(XmlNode node)
        {
            if (node.Name.Equals("artist"))
            {
                ID3ArtistFrame frame = new ID3ArtistFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("album"))
            {
                ID3AlbumFrame frame = new ID3AlbumFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("year"))
            {
                ID3YearFrame frame = new ID3YearFrame(Int16.Parse(node.InnerText));
                return frame;
            }
            else if (node.Name.Equals("genre"))
            {
                ID3GenreFrame frame = new ID3GenreFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("comment"))
            {
                ID3CommentsFrame frame = new ID3CommentsFrame(node.InnerText, node.Attributes["description"].Value, TextEncodingTypes.ISO88591);
                return frame;
            }
            else if (node.Name.Equals("involvedPeople"))
            {
                ID3InvolvedPeopleListFrame frame = new ID3InvolvedPeopleListFrame();
                foreach (XmlNode child in node.ChildNodes)
                {
                    if (node.Name.Equals("involvedPerson"))
                    {
                        frame.InvolvedPersonList.Add(child.InnerText, child.Attributes["type"].Value);
                    }
                    else
                    {
                        Console.WriteLine("Unexpected node nested within " + node.Name + ": " + child.Name);
                    }
                }
                return frame;
            }
            else if (node.Name.Equals("compactDiscIdentifier"))
            {
                //ID3CompactDiscIdentifierFrame frame = new ID3CompactDiscIdentifierFrame();
                //if (node.Attributes["MCDIFrameFormat"].Equals(MCDIFrameFormats.ID3Standard.ToString()))
                //{
                //    frame.MCDIFrameFormat = MCDIFrameFormats.ID3Standard;
                //    frame.HeaderBytes = StringToByteArray(node.Attributes["HeaderBytes"].Value);
                //    frame.LeadOutBytes = StringToByteArray(node.Attributes["LeadOutBytes"].Value);
                //    foreach (XmlNode child in node.ChildNodes)
                //    {
                //        if (child.Name.Equals("trackFrameAddress"))
                //        {
                //            frame.TrackFrameAddresses.Add(StringToByteArray(child.InnerText));
                //        }
                //        else
                //        {
                //            Console.WriteLine("Unexpected node nested within " + node.Name + ": " + child.Name);
                //        }
                //    }
                //}
                //else
                //{
                //    frame.MCDIFrameFormat = MCDIFrameFormats.WindowsMediaPlayer;
                //    frame.WindowsMediaPlayerCode = node.Attributes["WindowsMediaPlayerCode"].Value;
                //}
                //return frame;
            }
            else if (node.Name.Equals("private"))
            {
                ID3PrivateFrame frame = new ID3PrivateFrame(node.Attributes["owner"].Value, StringToByteArray(node.InnerText));
                return frame;
            }
            else if (node.Name.Equals("playCounter"))
            {
                ID3PlayCounterFrame frame = new ID3PlayCounterFrame(Int32.Parse(node.InnerText));
                return frame;
            }
            else if (node.Name.Equals("popularimeter"))
            {
                ID3PopularimeterFrame frame = new ID3PopularimeterFrame(Byte.Parse(node.InnerText), Int32.Parse(node.Attributes["counter"].Value), node.Attributes["email"].Value);
                return frame;
            }
            else if (node.Name.Equals("relativeVolumeAdjustment"))
            {
                ID3RelativeVolumeAdjustmentFrame frame = new ID3RelativeVolumeAdjustmentFrame();
                frame.BassChannelPeakVolume = Int16.Parse(node.Attributes["BassChannelPeakVolume"].Value);
                frame.BassChannelRelativeVolumeAdjustmentType = AttributeToEnum<RelativeVolumeAdjustmentType>(node.Attributes["BassChannelRelativeVolumeAdjustmentType"]);
                frame.BassChannelRelativeVolumeChange = Int16.Parse(node.Attributes["BassChannelRelativeVolumeChange"].Value);
                frame.CenterChannelPeakVolume = Int16.Parse(node.Attributes["CenterChannelPeakVolume"].Value);
                frame.CenterChannelRelativeVolumeAdjustmentType = AttributeToEnum<RelativeVolumeAdjustmentType>(node.Attributes["CenterChannelRelativeVolumeAdjustmentType"]);
                frame.CenterChannelRelativeVolumeChange = Int16.Parse(node.Attributes["CenterChannelRelativeVolumeChange"].Value);
                frame.LeftBackChannelPeakVolume = Int16.Parse(node.Attributes["LeftbackChannelPeakVolume"].Value);
                frame.LeftBackChannelRelativeVolumeAdjustmentType = AttributeToEnum<RelativeVolumeAdjustmentType>(node.Attributes["LeftbackChannelRelativeVolumeAdjustmentType"]);
                frame.LeftBackChannelRelativeVolumeChange = Int16.Parse(node.Attributes["LeftbackChannelRelativeVolumeChange"].Value);
                frame.LeftChannelPeakVolume = Int16.Parse(node.Attributes["LeftChannelPeakVolume"].Value);
                frame.LeftChannelRelativeVolumeAdjustmentType = AttributeToEnum<RelativeVolumeAdjustmentType>(node.Attributes["LeftChannelRelativeVolumeAdjustmentType"]);
                frame.LeftChannelRelativeVolumeChange = Int16.Parse(node.Attributes["LeftChannelRelativeVolumeChange"].Value);
                frame.RightBackChannelPeakVolume = Int16.Parse(node.Attributes["RightbackChannelPeakVolume"].Value);
                frame.RightBackChannelRelativeVolumeAdjustmentType = AttributeToEnum<RelativeVolumeAdjustmentType>(node.Attributes["RightbackChannelRelativeVolumeAdjustmentType"]);
                frame.RightBackChannelRelativeVolumeChange = Int16.Parse(node.Attributes["RightbackChannelRelativeVolumeChange"].Value);
                frame.RightChannelPeakVolume = Int16.Parse(node.Attributes["RightChannelPeakVolume"].Value);
                frame.RightChannelRelativeVolumeAdjustmentType = AttributeToEnum<RelativeVolumeAdjustmentType>(node.Attributes["RightChannelRelativeVolumeAdjustmentType"]);
                frame.RightChannelRelativeVolumeChange = Int16.Parse(node.Attributes["RightChannelRelativeVolumeChange"].Value);
                return frame;
            }
            else if (node.Name.Equals("reverb"))
            {
                ID3ReverbFrame frame = new ID3ReverbFrame();
                frame.PremixLeftToRight = Byte.Parse(node.Attributes["PremixLeftToRight"].Value);
                frame.PremixRightToLeft = Byte.Parse(node.Attributes["PremixRightToLeft"].Value);
                frame.ReverbBouncesLeft = Byte.Parse(node.Attributes["ReverbBouncesLeft"].Value);
                frame.ReverbBouncesRight = Byte.Parse(node.Attributes["ReverbBouncesRight"].Value);
                frame.ReverbFeedbackLeftToLeft = Byte.Parse(node.Attributes["ReverbFeedbackLeftToLeft"].Value);
                frame.ReverbFeedbackLeftToRight = Byte.Parse(node.Attributes["ReverbFeedbackLeftToRight"].Value);
                frame.ReverbFeedbackRightToLeft = Byte.Parse(node.Attributes["ReverbFeedbackRightToLeft"].Value);
                frame.ReverbFeedbackRightToRight = Byte.Parse(node.Attributes["ReverbFeedbackRightToRight"].Value);
                frame.ReverbLeftMS = Int16.Parse(node.Attributes["ReverbLeftMS"].Value);
                frame.ReverbRightMS = Int16.Parse(node.Attributes["ReverbRightMS"].Value);
                return frame;
            }
            else if (node.Name.Equals("synchronizedLyrics"))
            {
                ID3SynchronizedLyricsFrame frame = new ID3SynchronizedLyricsFrame();
                frame.ContentType = AttributeToEnum<SynchronizedLyricsContentTypes>(node.Attributes["ContentType"]);
                frame.Description = node.Attributes["Description"].Value;
                frame.Language = node.Attributes["Language"].Value;
                frame.PositionUnit = AttributeToEnum<SynchronizedLyricsPositionUnits>(node.Attributes["PositionUnit"]);
                foreach (XmlNode child in node.ChildNodes)
                {
                    if (child.Name.Equals("lyric"))
                    {
                        frame.SynchronizedLyrics.Add(child.InnerText, Int32.Parse(child.Attributes["position"].Value));
                    }
                    else
                    {
                        Console.WriteLine("Unexpected node nested within " + node.Name + ": " + child.Name);
                    }
                }
                return frame;
            }
            else if (node.Name.Equals("beatsPerMinute"))
            {
                ID3BeatsPerMinuteFrame frame = new ID3BeatsPerMinuteFrame(Int64.Parse(node.InnerText));
                return frame;
            }
            else if (node.Name.Equals("composers"))
            {
                ID3ComposersFrame frame = new ID3ComposersFrame();
                foreach (XmlNode child in node.ChildNodes)
                {
                    if (child.Name.Equals("composer"))
                    {
                        frame.Composers.Add(child.InnerText);
                    }
                    else
                    {
                        Console.WriteLine("Unexpected node nested within " + node.Name + ": " + child.Name);
                    }
                }
                return frame;
            }
            else if (node.Name.Equals("copyrightMessage"))
            {
                ID3CopyrightMessageFrame frame = new ID3CopyrightMessageFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("recordingDayMonth"))
            {
                ID3RecordingDayMonthFrame frame = new ID3RecordingDayMonthFrame(Byte.Parse(node.Attributes["month"].Value), Byte.Parse(node.Attributes["day"].Value));
                return frame;
            }
            else if (node.Name.Equals("playlistDelayMilliseconds"))
            {
                ID3PlaylistDelayMillisecondsFrame frame = new ID3PlaylistDelayMillisecondsFrame(Int64.Parse(node.InnerText));
                return frame;
            }
            else if (node.Name.Equals("encodedBy"))
            {
                ID3EncodedByFrame frame = new ID3EncodedByFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("lyricist"))
            {
                ID3LyricistFrame frame = new ID3LyricistFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("fileType"))
            {
                ID3FileTypeFrame frame = new ID3FileTypeFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("durationHoursMinutes"))
            {
                ID3DurationHoursMinutesFrame frame = new ID3DurationHoursMinutesFrame(TimeSpan.Parse(node.InnerText));
                return frame;
            }
            else if (node.Name.Equals("durationMilliseconds"))
            {
                ID3DurationMillisecondsFrame frame = new ID3DurationMillisecondsFrame(Int64.Parse(node.InnerText));
                return frame;
            }
            else if (node.Name.Equals("contentGroupDescription"))
            {
                ID3ContentGroupDescriptionFrame frame = new ID3ContentGroupDescriptionFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("title"))
            {
                ID3TitleFrame frame = new ID3TitleFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("subtitle"))
            {
                ID3SubtitleFrame frame = new ID3SubtitleFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("initialKey"))
            {
                ID3InitialKeyFrame frame = new ID3InitialKeyFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("languages"))
            {
                ID3LanguagesFrame frame = new ID3LanguagesFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("mediaType"))
            {
                ID3MediaTypeFrame frame = new ID3MediaTypeFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("originalAlbum"))
            {
                ID3OriginalAlbumFrame frame = new ID3OriginalAlbumFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("originalFileName"))
            {
                ID3OriginalFileNameFrame frame = new ID3OriginalFileNameFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("originalLyricist"))
            {
                ID3OriginalLyricistFrame frame = new ID3OriginalLyricistFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("originalArtist"))
            {
                ID3OriginalArtistFrame frame = new ID3OriginalArtistFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("originalYear"))
            {
                ID3OriginalYearFrame frame = new ID3OriginalYearFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("fileOwner"))
            {
                ID3FileOwnerFrame frame = new ID3FileOwnerFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("band"))
            {
                ID3BandFrame frame = new ID3BandFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("conductor"))
            {
                ID3ConductorFrame frame = new ID3ConductorFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("interpretedBy"))
            {
                ID3InterpretedByFrame frame = new ID3InterpretedByFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("partOfSet"))
            {
                ID3PartOfSetFrame frame = new ID3PartOfSetFrame(Byte.Parse(node.Attributes["PartNum"].Value), Byte.Parse(node.Attributes["PartCount"].Value));
                return frame;
            }
            else if (node.Name.Equals("publisher"))
            {
                ID3PublisherFrame frame = new ID3PublisherFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("trackNum"))
            {
                ID3TrackNumFrame frame = new ID3TrackNumFrame(Byte.Parse(node.Attributes["TrackNum"].Value), Byte.Parse(node.Attributes["TrackCount"].Value));
                return frame;
            }
            else if (node.Name.Equals("recordingDates"))
            {
                ID3RecordingDatesFrame frame = new ID3RecordingDatesFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("internetRadioStationName"))
            {
                ID3InternetRadioStationNameFrame frame = new ID3InternetRadioStationNameFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("internetRadioStationOwner"))
            {
                ID3InternetRadioStationOwnerFrame frame = new ID3InternetRadioStationOwnerFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("sizeBytes"))
            {
                ID3SizeBytesFrame frame = new ID3SizeBytesFrame(Int64.Parse(node.InnerText));
                return frame;
            }
            else if (node.Name.Equals("internationalStandardRecordingCode"))
            {
                ID3InternationalStandardRecordingCodeFrame frame = new ID3InternationalStandardRecordingCodeFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("encoderSettings"))
            {
                ID3EncoderSettingsFrame frame = new ID3EncoderSettingsFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("userDefinedText"))
            {
                ID3UserDefinedTextFrame frame = new ID3UserDefinedTextFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("uniqueFileID"))
            {
                ID3UniqueFileIDFrame frame = new ID3UniqueFileIDFrame(node.Attributes["ownerID"].Value, StringToByteArray(node.InnerText));
                return frame;
            }
            else if (node.Name.Equals("unsyncedLyrics"))
            {
                ID3UnsynchedLyricsFrame frame = new ID3UnsynchedLyricsFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("commercialInformationWebAddress"))
            {
                ID3CommercialInformationWebAddressFrame frame = new ID3CommercialInformationWebAddressFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("copyrightInformationWebAddress"))
            {
                ID3CopyrightInformationWebAddressFrame frame = new ID3CopyrightInformationWebAddressFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("officialAudioFileWebAddress"))
            {
                ID3OfficialAudioFileWebAddressFrame frame = new ID3OfficialAudioFileWebAddressFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("officialArtistWebAddress"))
            {
                ID3OfficialArtistWebAddressFrame frame = new ID3OfficialArtistWebAddressFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("officialAudioSourceWebAddress"))
            {
                ID3OfficialAudioSourceWebAddressFrame frame = new ID3OfficialAudioSourceWebAddressFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("officialInternetRadioStationWebAddress"))
            {
                ID3OfficialInternetRadioStationWebAddressFrame frame = new ID3OfficialInternetRadioStationWebAddressFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("paymentWebAddress"))
            {
                ID3PaymentWebAddressFrame frame = new ID3PaymentWebAddressFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("publisherWebAddress"))
            {
                ID3PublisherWebAddressFrame frame = new ID3PublisherWebAddressFrame(node.InnerText);
                return frame;
            }
            else if (node.Name.Equals("userDefinedWebAddress"))
            {
                ID3UserDefinedWebAddressFrame frame = new ID3UserDefinedWebAddressFrame(node.InnerText, node.Attributes["description"].Value);
                return frame;
            }
            return null;
        }


        private static byte[] StringToByteArray(string value)
        {
            if (value.Length == 0)
            {
                return new byte[0];
            }
            string[] values = value.Split(' ');
            byte[] bytes = new byte[values.Length];
            for (int i = 0; i < values.Length; i++)
            {
                bytes[i] = Byte.Parse(values[i], System.Globalization.NumberStyles.HexNumber);
            }
            return bytes;
        }


        private static String ByteArrayToString(byte[] bytes)
        {
            if (bytes.Length == 0)
            {
                return "";
            }
            StringBuilder value = new StringBuilder();
            foreach (byte b in bytes)
            {
                value.Append(b.ToString("X"));
                value.Append(" ");
            }
            value.Remove(value.Length - 1, 1);
            return value.ToString();
        }


        private static EnumType AttributeToEnum<EnumType>(XmlAttribute attr)
        {
            return (EnumType)Enum.Parse(typeof(EnumType), attr.Value, true);
        }
    }
}
